mod config;
mod handlers;
mod middleware;

mod models;

mod error;
mod utils;

use std::{fmt, ops::Deref, sync::Arc};
use anyhow::Context;
use axum::{middleware::from_fn_with_state, Router, routing::{get, post, patch}};
use sqlx::PgPool;

pub use config::{AppConfig, AuthConfig};
pub use utils::{DecodingKey, EncodingKey};
pub use handlers::*;
pub use middleware::*;
pub use models::User;
pub use error::{AppError, ErrorOutput};
use verify_token;

#[derive(Debug, Clone)]
pub(crate) struct AppState {
    inner: Arc<AppStateInner>,
}

pub(crate) struct AppStateInner {
    pub(crate) app_config: AppConfig,
    pub(crate) ek: EncodingKey,
    pub(crate) dk: DecodingKey,
    pub(crate) pool: PgPool,
}

pub async fn get_router(app_config: AppConfig) -> Result<Router, AppError> {
    let app_state = AppState::try_new(app_config).await?;
    let api = Router::new()
        .route("/chat", get(list_chat_handler).post(create_chat_handler))
        .route(
            "/chat/:id",
            patch(update_chat_handler)
                .delete(delete_chat_handler)
                .post(send_msg_handler),
        ).route("/chat/:id/messages", get(list_msg_handler))
        .layer(from_fn_with_state(app_state.clone(), verify_token))
        .route("/signin", post(signin_handler))
        .route("/signup", post(signup_handler));

    let r = Router::new()
        .route("/", get(index_handler))
        .nest("/api", api)
        .with_state(app_state);

    Ok(set_layer(r))
}

impl Deref for AppState {
    type Target = AppStateInner;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl AppState {
    pub async fn try_new(app_config: AppConfig) -> Result<Self, AppError> {
        let ek = EncodingKey::load(&app_config.auth.sk).context("failed to load ek")?;
        let dk = DecodingKey::load(&app_config.auth.pk).context("failed to load dk")?;

        let pool = PgPool::connect(&app_config.server.db_url).await?;
        let u = Self {
            inner: Arc::new(AppStateInner {
                app_config,
                ek,
                dk,
                pool,
            })
        };

        Ok(u)
    }
}

impl fmt::Debug for AppStateInner {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AppStateInner")
            .field("app_config", &self.app_config)
            .finish()
    }
}